home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: DZGame.c
- * Author: Dan Venolia
- *
- * Contents: Handles the game play.
- *
- * Copyright © 1996 Apple Computer, Inc.
- */
-
- #include <assert.h>
- #include <string.h>
-
- #include <Events.h>
- #include <Fonts.h>
- #include <QDOffscreen.h>
- #include <Timer.h>
- #include <Types.h>
-
- #include <QD3D.h>
- #include <QD3DGeometry.h>
- #include <QD3DMath.h>
- #include <QD3DSet.h>
-
- #include "SoundSprocket.h"
- #include "InputSprocket.h"
-
- #include "DZDisplay.h"
- #include "DZDrone.h"
- #include "DZGame.h"
- #include "DZKeyCap.h"
- #include "DZSound.h"
- #include "DZSpace.h"
-
-
-
- #define DEFAULT_INTERVAL 0.05 // Initial frame rate -- just a guess
- #define MAX_INTERVAL 0.25 // Limit on seconds per frame
-
- #define KEY_ROTATE_RATE 1.0 // Radians per second while key is down
- #define INV_SQRT_TWO 0.707
-
- #define AXIS_ROTATE_RATE 3.0 // Radians per second for full joystick throw
-
-
- enum {
- kGameAutoDroneCount = 4 // Number of autopilot drones
- };
-
-
- float gGameInterval = DEFAULT_INTERVAL;
- float gGameFramesPerSecond = 1.0/DEFAULT_INTERVAL;
-
- static unsigned long gGameTime = 0; // Last frame time
-
- static short gGameDirectionalKeyCap = 0; // Last directional keycap pressed
- static Boolean gGameDirectionalKeyDown = false; // Pressed recently?
-
- static Boolean gGameHUDVisible = true;
-
- static TDroneObject gGameSelfDrone = NULL;
-
- static GWorldPtr gGameFPSGWorld = NULL;
- static TQ3GeometryObject gGameFPSMarker = NULL;
- static TQ3MarkerData gGameFPSMarkerData;
- static Boolean gGameFPSVisible = false;
-
- static TQ3GeometryObject gGameCrossHairs = NULL;
- static unsigned long gGameCrossHairsData[32] = {
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00010000, 0x00000000, 0x00010000,
- 0x00000000, 0x00054000, 0x00101000, 0x00228800,
- 0x00082000, 0x00400400, 0x00101000, 0x05400540,
- 0x00101000, 0x00400400, 0x00082000, 0x00228800,
- 0x00101000, 0x00054000, 0x00000000, 0x00010000,
- 0x00000000, 0x00010000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000
- };
-
-
- static void Game_DirectionalKey(
- void);
-
- static OSErr Game_TwoButtonJoystick(SInt32 *xAxis, SInt32 *yAxis,
- SInt32 *POVHat,
- Boolean *button1, Boolean *button2);
-
- /* =============================================================================
- * Game_TwoButtonJoystick (internal)
- *
- * Grab the joystick axes and buttons.
- * ========================================================================== */
- OSErr Game_TwoButtonJoystick(SInt32 *xAxis, SInt32 *yAxis,
- SInt32 *POVHat,
- Boolean *button1, Boolean *button2)
- {
- static UInt32 status = 0;
- static ElementReference lastXAxis = nil;
- static ElementReference lastYAxis = nil;
- Boolean button1MustBeDown;
- Boolean button2MustBeDown;
- int maxEvents;
- ElementEvent theEvent;
- Boolean wasEvent;
- OSErr err;
- ElementListReference globalList;
- ElementInfo info;
-
- *button1 = false;
- *button2 = false;
-
- status = 0;
-
- button1MustBeDown = false;
- button2MustBeDown = false;
-
- maxEvents = 10; // only do events ten at a time, they might be continuously streaming out
- wasEvent = true;
- err = noErr;
-
- err = GetGlobalElementList(&globalList);
-
- while(maxEvents > 0)
- {
- err = ElementListGetNextEvent(globalList, sizeof(theEvent), &theEvent, &wasEvent);
-
- if ((wasEvent == false) || (err))
- {
- break;
- }
-
- // get information about this element since we are not doing any configuration
- // we need to know the about labels and kinds
- GetElementInfo(theEvent.element, &info);
-
- switch(info.theKind)
- {
- case kElementKindButton:
- {
- ButtonConfigurationInfo buttonConfig;
- Boolean newValue;
-
- // figure out the new value for the boolean
- if (theEvent.data)
- {
- newValue = true;
- }
- else
- {
- newValue = false;
- }
-
- // need configuration information to determine if this is button1 or not
- GetElementConfigurationInfo(theEvent.element, sizeof(buttonConfig), &buttonConfig);
-
- // we will call button id 1 our buttons 1, usually the trigger
- if (buttonConfig.id == 1)
- {
- if (newValue)
- {
- *button1 = true;
- }
- }
- else
- {
- if (newValue)
- {
- *button2 = true;
- }
- }
- }
- break;
-
- case kElementKindDPad:
- {
- if (info.theLabel == kElementLabelPOVHat)
- {
- *POVHat = theEvent.data;
- }
- else if (info.theLabel == kElementLabelPadMove)
- {
- // a movement DPad so they might be using
- // a console style direction pad so turn
- // that into axis style data
-
- lastXAxis = nil;
- lastYAxis = nil;
-
- switch(theEvent.data)
- {
- case kPadIdle:
- *xAxis = kAxisCenter;
- *yAxis = kAxisCenter;
- break;
- case kPadLeft:
- *xAxis = kAxisMinimum;
- *yAxis = kAxisCenter;
- break;
- case kPadUpLeft:
- *xAxis = kAxisMinimum;
- *yAxis = kAxisMaximum;
- break;
- case kPadUp:
- *xAxis = kAxisCenter;
- *yAxis = kAxisMaximum;
- break;
- case kPadUpRight:
- *xAxis = kAxisMaximum;
- *yAxis = kAxisMaximum;
- break;
- case kPadRight:
- *xAxis = kAxisMaximum;
- *yAxis = kAxisCenter;
- break;
- case kPadDownRight:
- *xAxis = kAxisMaximum;
- *yAxis = kAxisMinimum;
- break;
- case kPadDown:
- *xAxis = kAxisCenter;
- *yAxis = kAxisMinimum;
- break;
- case kPadDownLeft:
- *xAxis = kAxisMinimum;
- *yAxis = kAxisMinimum;
- }
- }
- }
- break;
-
- case kElementKindAxis:
- {
- // if it is an axis find out if it is the
- // x or y axis style data and use that.
-
- if (info.theLabel == kElementLabelXAxis)
- {
- lastXAxis = theEvent.element;
-
- *xAxis = theEvent.data;
- }
- else if (info.theLabel == kElementLabelYAxis)
- {
- lastYAxis = theEvent.element;
-
- *yAxis = theEvent.data;
- }
- }
- break;
- }
-
- maxEvents--;
- }
-
- // poll the last meaningful axis we had on the way out
- // just to make sure we had good data
- if (lastXAxis != nil)
- {
- GetElementSimpleState(lastXAxis, xAxis);
- }
-
- if (lastYAxis != nil)
- {
- GetElementSimpleState(lastYAxis, yAxis);
- }
-
- return err;
- }
-
-
- /* =============================================================================
- * Game_Init (external)
- *
- * Initializes the game stuff.
- * ========================================================================== */
- void Game_Init(
- void)
- {
- int droneNum;
- Rect bounds;
- PixMapHandle pixMapHandle;
- CGrafPtr savePort;
- GDHandle saveGDevice;
- TQ3MarkerData markerData;
- TQ3ColorRGB color;
-
- // Create the drone for the player
- gGameSelfDrone = SelfDrone_New();
-
- // Create some automatic drones
- for (droneNum = 0; droneNum < kGameAutoDroneCount; droneNum++)
- {
- AutoDrone_New(gGameSelfDrone);
- }
-
- // Set up the 3D sound listener
- Sound_GetListener();
-
- // Create the FPS marker
- bounds.top = 0;
- bounds.left = 0;
- bounds.bottom = 12;
- bounds.right = 31;
-
- gGameFPSGWorld = NULL;
- NewGWorld(&gGameFPSGWorld, 1, &bounds, NULL, NULL, 0);
- assert(gGameFPSGWorld != NULL);
-
- LockPixels(gGameFPSGWorld->portPixMap);
-
- GetGWorld(&savePort, &saveGDevice);
- SetGWorld(gGameFPSGWorld, NULL);
-
- TextFont(geneva);
- TextSize(10);
-
- EraseRect(&gGameFPSGWorld->portRect);
-
- SetGWorld(savePort, saveGDevice);
-
- pixMapHandle = GetGWorldPixMap(gGameFPSGWorld);
-
- gGameFPSMarkerData.location.x = 0.0;
- gGameFPSMarkerData.location.y = 0.0;
- gGameFPSMarkerData.location.z = 0.0;
- gGameFPSMarkerData.xOffset = -(bounds.left+bounds.right+1 >> 1);
- gGameFPSMarkerData.yOffset = -(bounds.top+bounds.bottom+1 >> 1);
- gGameFPSMarkerData.bitmap.image = (unsigned char*) GetPixBaseAddr(pixMapHandle);
- gGameFPSMarkerData.bitmap.width = bounds.right-bounds.left;
- gGameFPSMarkerData.bitmap.height = bounds.bottom-bounds.top;
- gGameFPSMarkerData.bitmap.rowBytes = (*pixMapHandle)->rowBytes & 0x00003FFF;
- gGameFPSMarkerData.bitmap.bitOrder = kQ3EndianBig;
- gGameFPSMarkerData.markerAttributeSet = Q3AttributeSet_New();
- assert(gGameFPSMarkerData.markerAttributeSet != NULL);
-
- color.r = 1.0;
- color.g = 1.0;
- color.b = 0.4;
-
- Q3AttributeSet_Add(gGameFPSMarkerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color);
-
- gGameFPSMarker = Q3Marker_New(&gGameFPSMarkerData);
-
- // Create the crosshairs
- markerData.location.x = 0.0;
- markerData.location.y = 0.0;
- markerData.location.z = 0.0;
- markerData.xOffset = -15;
- markerData.yOffset = -15;
- markerData.bitmap.image = (unsigned char*) gGameCrossHairsData;
- markerData.bitmap.width = 31; //• SHOULD BE 32, BUT TO GET AROUND A APPLE QD3D ACCEL CARD DRIVER BUG...
- markerData.bitmap.height = 32;
- markerData.bitmap.rowBytes = 4;
- markerData.bitmap.bitOrder = kQ3EndianBig;
- markerData.markerAttributeSet = Q3AttributeSet_New();
- assert(markerData.markerAttributeSet != NULL);
-
- color.r = 1.0;
- color.g = 1.0;
- color.b = 0.4;
-
- Q3AttributeSet_Add(markerData.markerAttributeSet, kQ3AttributeTypeDiffuseColor, &color);
-
- gGameCrossHairs = Q3Marker_New(&markerData);
-
- Q3Object_Dispose(markerData.markerAttributeSet);
- markerData.markerAttributeSet = NULL;
- }
-
-
- /* =============================================================================
- * Game_Exit (external)
- *
- * Prepares for exit.
- * ========================================================================== */
- void Game_Exit(
- void)
- {
- TDroneObject drone;
-
- while ((drone = Drone_Next(NULL)) != NULL)
- {
- Drone_Dispose(drone);
- }
-
- if (gGameFPSMarker != NULL)
- {
- Q3Object_Dispose(gGameFPSMarker);
- gGameFPSMarker = NULL;
- }
-
- if (gGameCrossHairs != NULL)
- {
- Q3Object_Dispose(gGameCrossHairs);
- gGameCrossHairs = NULL;
- }
- }
-
-
- /* =============================================================================
- * Game_KeyDown (external)
- *
- * Handles a key press. The fire button happens immediately. The most
- * recently pressed of the directional keys is remembered for use in
- * Game_DirectionalKey.
- * ========================================================================== */
- void Game_KeyDown(
- unsigned long inChar,
- unsigned long inKeyCap)
- {
- switch (inKeyCap)
- {
- case kKeyCap_Numeric1:
- case kKeyCap_Numeric2:
- case kKeyCap_Numeric3:
- case kKeyCap_Numeric4:
- case kKeyCap_Numeric6:
- case kKeyCap_Numeric7:
- case kKeyCap_Numeric8:
- case kKeyCap_Numeric9:
- gGameDirectionalKeyCap = inKeyCap;
- gGameDirectionalKeyDown = true;
- break;
-
- case kKeyCap_SpaceBar:
- Drone_Fire(gGameSelfDrone);
- break;
-
- default:
- switch (inChar)
- {
- case 'h':
- case 'H':
- gGameHUDVisible = !gGameHUDVisible;
- break;
-
- case 'f':
- case 'F':
- gGameFPSVisible = !gGameFPSVisible;
- break;
- }
- }
- }
-
-
- /* =============================================================================
- * Game_Process (external)
- *
- * Handles idle time by moving the game ahead one time step. Only called if
- * the game is active.
- * ========================================================================== */
- void Game_Process(
- void)
- {
- UnsignedWide wide;
- unsigned long now;
- Str15 str;
- CGrafPtr savePort;
- GDHandle saveGDevice;
- TDroneObject drone;
- TDroneObject next;
- TQ3Point3D position;
- TQ3Vector3D direction;
- TQ3Vector3D up;
- TQ3Matrix4x4 matrix;
- SInt32 xAxis;
- SInt32 yAxis;
- SInt32 POVHat;
- Boolean button1;
- Boolean button2;
-
- // Find the frame rate
- Microseconds(&wide);
- now = wide.lo;
-
- if (gGameTime != 0)
- {
- // Find the interval for the last frame
- gGameInterval = 0.000001*(now-gGameTime);
-
- // Limit frame rate to a resonable number
- if (gGameInterval > MAX_INTERVAL)
- {
- gGameInterval = MAX_INTERVAL;
- }
-
- // Find corresponding frames per second
- gGameFramesPerSecond = 1.0/gGameInterval;
- }
-
- gGameTime = now;
-
- // Update the FPS marker
- if (gGameFPSVisible)
- {
- sprintf((char*) str, "x%.1f", gGameFramesPerSecond);
- str[0] = strlen((char*) str) - 1;
-
- GetGWorld(&savePort, &saveGDevice);
- SetGWorld(gGameFPSGWorld, NULL);
-
- EraseRect(&gGameFPSGWorld->portRect);
-
- MoveTo(gGameFPSGWorld->portRect.left+gGameFPSGWorld->portRect.right-StringWidth(str) >> 1, 10);
- DrawString(str);
-
- SetGWorld(savePort, saveGDevice);
-
- Q3Marker_SetBitmap(gGameFPSMarker, &gGameFPSMarkerData.bitmap);
- }
-
- // Change direction while key is held down
- Game_DirectionalKey();
-
- // use the InputSprocket
- Game_TwoButtonJoystick(&xAxis, &yAxis, &POVHat, &button1, &button2);
-
- SelfDrone_Turn(
- gGameSelfDrone,
- ((float) xAxis / (float) kAxisMaximum)*AXIS_ROTATE_RATE*gGameInterval,
- ((float) yAxis / (float) kAxisMaximum)*AXIS_ROTATE_RATE*gGameInterval);
-
- if (button1)
- {
- Drone_Fire(gGameSelfDrone);
- }
-
- if (button2)
- {
- gGameHUDVisible = !gGameHUDVisible;
- }
-
- // Move all the drones, mark any to be deleted
- for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone))
- {
- Drone_Move(drone);
- }
-
- // Delete the marked drones
- drone = Drone_Next(NULL);
- while (drone != NULL)
- {
- next = Drone_Next(drone);
-
- if (Drone_GetMark(drone))
- {
- Drone_Dispose(drone);
- }
-
- drone = next;
- }
-
- // Move the viewer to follow the self drone
- Drone_GetPosition(gGameSelfDrone, &position);
- Drone_GetDirection(gGameSelfDrone, &direction);
- Drone_GetUp(gGameSelfDrone, &up);
-
- Display_SetViewerPosition(&position, &direction, &up);
-
- // Move the listener to follow the self drone
- Drone_GetMatrix(gGameSelfDrone, &matrix);
- SetListenerTransform(Sound_GetListener(), &matrix);
-
- // Change the localized sounds
- for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone))
- {
- Drone_UpdateSound(drone);
- }
- }
-
-
- /* =============================================================================
- * Game_Submit (external)
- *
- * Submits all the 3D geometry of the game.
- * ========================================================================== */
- void Game_Submit(
- TQ3ViewObject inView)
- {
- TDroneObject drone;
- TQ3Point3D position;
- TQ3Vector3D direction;
- TQ3Vector3D up;
- TQ3Vector3D v;
- TQ3Point3D markerPosition;
-
- assert(inView != NULL);
-
- // Submit the drones
- for (drone = Drone_Next(NULL); drone != NULL; drone = Drone_Next(drone))
- {
- Drone_Submit(drone, gGameHUDVisible, inView);
- }
-
- // Get information about the camera position
- Drone_GetPosition(gGameSelfDrone, &position);
- Drone_GetDirection(gGameSelfDrone, &direction);
- Drone_GetUp(gGameSelfDrone, &up);
-
- // Submit the spacejunk
- Space_Submit(inView, &position, &direction);
-
- // Submit the FPS marker if the caps lock key is down
- if (gGameFPSVisible)
- {
- Q3Point3D_Vector3D_Add(&position, &direction, &markerPosition);
- Q3Vector3D_Scale(&up, -0.6, &v);
- Q3Point3D_Vector3D_Add(&markerPosition, &v, &markerPosition);
- Q3Marker_SetPosition(gGameFPSMarker, &markerPosition);
- Q3Object_Submit(gGameFPSMarker, inView);
- }
-
- // Submit the crosshairs
- if (gGameHUDVisible)
- {
- Q3Point3D_Vector3D_Add(&position, &direction, &markerPosition);
- Q3Marker_SetPosition(gGameCrossHairs, &markerPosition);
- Q3Object_Submit(gGameCrossHairs, inView);
- }
- }
-
-
- /* =============================================================================
- * Game_DirectionalKey (internal)
- *
- * Takes action based on the currently pressed directional key. If the most
- * recently pressed key is still down, or if this is the first time for the
- * key, then the main drone is turned.
- * ========================================================================== */
- void Game_DirectionalKey(
- void)
- {
- Boolean keyIsDown;
- KeyMap keyMap;
- unsigned long index;
- unsigned long mask;
- float horz;
- float vert;
-
- // Do we even have a key to check?
- if (gGameDirectionalKeyCap != 0)
- {
- // Check the state of the directional key that went down last
- keyIsDown = false;
-
- if (gGameDirectionalKeyDown)
- {
- // Key was pressed on this turn
- keyIsDown = true;
- gGameDirectionalKeyDown = false;
- }
- else
- {
- // Key was pressed some time ago -- is it still down?
- GetKeys(keyMap);
-
- // KeyMap is scrambled because of funky old Pascal bit packing rules.
- // The keycap may be decomposed as follows:
- //
- // +---+---+---+---|---+---+---+---+
- // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
- // +---+---+---+---|---+---+---+---+
- // | 0 | index | ~byte | bit |
- // +---+---+---+---|---+---+---+---+
-
- index = gGameDirectionalKeyCap >> 5 & 0x03;
- mask = 1 << ((gGameDirectionalKeyCap & 0x1F) ^ 0x18);
-
- if (keyMap[index] & mask)
- {
- // Key is still down
- keyIsDown = true;
- }
- else
- {
- // The key has been released
- gGameDirectionalKeyCap = 0;
-
- //• TODO: Search for any other directional key down
- }
- }
-
- // Do the action while the directional key is still down
- if (keyIsDown)
- {
- // Find the amount of turn
- horz = 0.0;
- vert = 0.0;
-
- switch (gGameDirectionalKeyCap)
- {
- case kKeyCap_Numeric1:
- horz = -INV_SQRT_TWO*KEY_ROTATE_RATE;
- vert = horz;
- break;
-
- case kKeyCap_Numeric2:
- vert = -KEY_ROTATE_RATE;
- break;
-
- case kKeyCap_Numeric3:
- horz = INV_SQRT_TWO*KEY_ROTATE_RATE;
- vert = -horz;
- break;
-
- case kKeyCap_Numeric4:
- horz = -KEY_ROTATE_RATE;
- break;
-
- case kKeyCap_Numeric6:
- horz = KEY_ROTATE_RATE;
- break;
-
- case kKeyCap_Numeric7:
- vert = INV_SQRT_TWO*KEY_ROTATE_RATE;
- horz = -vert;
- break;
-
- case kKeyCap_Numeric8:
- vert = KEY_ROTATE_RATE;
- break;
-
- case kKeyCap_Numeric9:
- horz = INV_SQRT_TWO*KEY_ROTATE_RATE;
- vert = horz;
- break;
- }
-
- // Execute the turn
- SelfDrone_Turn(gGameSelfDrone, horz*gGameInterval, vert*gGameInterval);
- }
- }
- }
-
-
-